package mathy.wili.extract_srcFile.java;
import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import _a.Config26;
import javassist.ClassClassPath;
import javassist.NotFoundException;
import mathy.c.Ca;
import mathy.c.LogConf;
import mathy.wili.autoeq.Refs;
import mathy.wili.c.Class9;
import mathy.wili.c.File9;
import mathy.wili.c.Misc9;
import mathy.wili.c.asm.AsmItems;
/**
 * 	汇集 java源码中各个函数的起止范围及行号信息。
 * @author weila 2021年2月12日
 */
public class RangesOfClsMember implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 *  main|first class in file.
	 */
	final Class<?> mainClass;

	public boolean visited;

	final File file;

	final String fileRelativePath;

	/**
	 * null value if clazz.isEnum() || clazz.isAnnotation().
	 */
	public final RootOfClsMember root;

	/**
	 * .包括java文件中依次出现的成员：本地类以外的所有内部类|类中方法|注解|注释<br>
	 * 	Can't use lineNo ~> MethodGeter, because lineNo is not accurate in javassist.jar!<br>
	 * .首行日志根据行号可从中搜索到方法对象。
	 *   	
	 */
	final List<ClsMemberRange> ranges;
	//
	public String toString() {
		String st = ranges.size() + "[" + Misc9.strOfList(ranges, -1, "\n ") + "\n]";
		System.out.println(st);
		return st;
	}

	public RangesOfClsMember(String relative, Class<?> clazz, File file) throws NotFoundException {
		//取得所有函数的起止下标。
		this.fileRelativePath = relative;
		Ca.asert(relative != null, clazz);
		this.mainClass = clazz;
		this.file = file;
		if (clazz.isEnum() || clazz.isAnnotation()) {
			Cont26.addMustBeCopiedFile(clazz);
			root = null;
			ranges = null;
			return;
		}
		String fileSt = File9.strOfFile(file);
		RootOfClsMember root = new RootOfClsMember(clazz, fileSt, true);
		if (clazz.equals(Misc9.class))//Test_match_Java8_main_____.class
			Ca.pause();
		this.root = root;
		Ca.pause();
		this.ranges = root.getClsMemberRangeList();
		Ca.pause();
	}

	public ClsRange getClsRange(Class<?> cls) {
		if (root == null)
			return null;
		ClsRange ran = root.getClsRangeMap().get(cls);
		return ran;
	}

	public FieldRange getFieldRange(Class<?> cls, String field) {
		ClsRange crange = this.getClsRange(cls);
		if (crange == null)
			return null;
		return crange.getFieldRange(field);
	}

	public MethodRange getMethodRange(Executable md) {
		if (root == null)
			return null;
		Map<Class<?>, ClsRange> map = this.root.getClsRangeMap();
		//map.keySet();
		ClsRange ran = map.get(md.getDeclaringClass());
		return ran.getMethodRange(md);
	}

	public static RangesOfClsMember getInst(int depth, Class<?> cls) {
		Class<?> headCls = Cont26.ClsToHeadClsMap.get(cls);
		if (headCls != null && headCls != cls) {
			return RangesOfClsMember.getInst(-1, headCls);
		}
		RangesOfClsMember ret = RangesOfClsMember.getInst00(-1, cls);
		if (ret != null)
			return ret;
		return null;
	}

	private static RangesOfClsMember getInst00(int depth, Class<?> cls) {
		++depth;
		if (cls.isArray() || cls.isPrimitive() || cls.isAnnotation())
			return null;
		if (!Config26.Class_filter.accept(cls))
			return null;
		cls = Class9.rootClsOf(cls);
		RangesOfClsMember inst = CodeExtract.DATA_MAP.get(cls);
		if (inst != null)
			return inst;
		if (cls.equals(AsmItems.class))
			Ca.pause();
		File file = null;
		CodeExtract.ClsPool.insertClassPath(new ClassClassPath(cls));//else javassist.NotFoundException.
		File[] roots = LogConf.getRootDirs_findFile();
		for (int i = 0; i < roots.length; i++) {
			File root = roots[i];
			File[] ff = File9.javafileOfClass(cls, roots[i]);
			if (ff != null) {
				root = ff[0];
				file = ff[1];
				try {
					Ca.pause();
					String relative = File9.relativePathOf(root, file);
					inst = new RangesOfClsMember(relative, cls, file);//<<<<<<<<<<<<<<<<<<
					if (inst.root != null) {
						Coms26.visit_all_simple_staticFinalField_fromImports_and_samePackage(inst.root);
						ClsRange cr = inst.getClsRange(cls);
						cr.setVisited();
					}
					CodeExtract.DATA_MAP.put(cls, inst);
					CodeExtract.DATA_LIST.add(inst);
					return inst;
				} catch (NotFoundException e) {
					e.printStackTrace();
					throw new RuntimeException(e);
				}
			}
		} //
		return null;
	}

	/**
	 *	追加，略过上方法|属性|类到本方法之间的注解
	 */
	void append_untilePreMemberEnd(int fromMemberIndex, ClsMemberRange curMember, int fromStrIndex, StringBuilder sb) {
		ClsMemberRange preMember = ClsMemberRange.findPreSibling(fromMemberIndex, this.ranges, curMember,
				(ClsMemberRange item) -> {
					if (item instanceof FieldRange || item instanceof MethodRange || item instanceof ClsRange
							|| item instanceof InitBlockRange)
						return true;
					return false;
				});
		String fileSt = curMember.root.fileSt0;
		if (preMember == null) {
			sb.append(fileSt.substring(fromStrIndex, curMember.aStart));
		} else if (preMember.depth < curMember.depth) {
			//前成员更浅
			if (preMember instanceof ClsRange) {
				ClsRange preRange = (ClsRange) preMember;
				sb.append(fileSt.substring(fromStrIndex, preRange.bodyStart + 1));
			} else {//eg. preMember is "import wili.al.antlr.java8.coda.VarJ", is FieldRange :(
				//前成员非类
				sb.append(fileSt.substring(fromStrIndex, curMember.aEnd));
			}
		} else if (preMember.depth == curMember.depth) {
			sb.append(fileSt.substring(fromStrIndex, preMember.aEnd));
		} else {//
			sb.append(fileSt.substring(fromStrIndex, preMember.aEnd));
		}
	}
	Predicate<Method> pred = null;
	static Constructor<?> minConstructorOf(Class<?> cls) {
		if (cls.isInterface())
			return null;
		Constructor<?>[] cc = cls.getDeclaredConstructors();
		if (cc.length == 1)
			return cc[0];
		if (cc.length == 0)
			return null;
		int num = cc[0].getParameterCount(), minInd = 0;
		for (int i = 1; i < cc.length; i++) {
			Constructor<?> ele = cc[i];
			if (ele.getParameterCount() < num) {
				num = ele.getParameterCount();
				minInd = i;
			}
		}
		return cc[minInd];
	}

	/**
	 *  Write visited elements to java file.
	 */
	void writeMeToJavaFile() {
		//只移除那些在 methodMap 集中的，但visit属性为false的方法。
		if (mainClass.equals(CodeExtract.class))
			Refs.ref();
		File tofile = new File(Config26.Extract_toSourceDir, this.fileRelativePath);
		if ("".isEmpty() && tofile.exists()) {
			Ca.warn(1, "\t", "file already exist:", tofile.getAbsoluteFile());
			return;
		}
		Coms26.addMsg(-1, "writeVisitedClassMemberToJavaFile:", File9.name2(file));
		String fileSt = File9.strOfFile(file);
		StringBuilder sb = new StringBuilder(fileSt.length());
		int from = 0;
		MethodRange pre = null;
		{//确保提取一个构造函数
			if (!Cont26.useAsmOnly && !mainClass.isInterface()) {
				Constructor<?> con = minConstructorOf(mainClass);
				MethodRange ran = this.getMethodRange(con);
				if (!ran.visited) {
					ran.setVisited();
				}
			}
		}
		{//若属性定义时取值于一个【静态常量简单值属性】，则访问该值
			for (int i = 0; i < this.ranges.size(); i++) {
				ClsMemberRange mem = ranges.get(i);
				if (mem instanceof FieldRange) {
					FieldRange field = (FieldRange) mem;
					if (Cont26.filesMustBeCopied.contains(field.get().getDeclaringClass()))
						continue;
					if (field.name.equals("LogLevel"))
						Ca.pause();
					if (field.isStaticFinalSimpleValue)
						field.setVisited();
					if (field.visited) {
						if (field.referedStaticFinalField != null) {//常量字段
							Visit26.visitField(-1, field.referedStaticFinalField.getField());
						}
					}
				}
			}
		}
		f1: for (int i = 0; i < this.ranges.size(); i++) {//包括依次出现的成员：本地类以外的所有内部类|类中方法|注解|注释
			if (i == 99)
				Ca.pause();
			ClsMemberRange mem = ranges.get(i);//mem.root.toString()
			if (Cont26.filesMustBeCopied.contains(mem.root.clazz))
				continue;
			if (mem instanceof FieldRange) {
				FieldRange field = (FieldRange) mem;
				if (field.name.equals("UseLog"))
					Ca.pause();
				if (field.visited) {
				} else {
					append_untilePreMemberEnd(i - 1, field, from, sb);
					from = field.aEnd;
				}
				continue;
			} else if (mem instanceof AnnotationRange || mem instanceof NotationRange) {
				continue;
			} else if (mem instanceof InitBlockRange) {
				if (((InitBlockRange) mem).isStatic) {
					sb.append(fileSt.substring(from, mem.aEnd));//无条件访问静态块
					from = mem.aEnd;
				} else {
					sb.append(fileSt.substring(from, mem.aStart));
					from = mem.aStart;
					if (mem.clsRange.objInited) {
					} else {
						from = mem.aEnd;
					}
				}
				if (from == 206382)
					Ca.pause();
				continue;
			} else if (mem instanceof ClsRange) {
				ClsRange range = (ClsRange) mem;
				if (this.mainClass.equals(range.cls)) {
					Ca.pause();
				} else if (!range.visited && !range.isEmpty()) {
					append_untilePreMemberEnd(i - 1, range, from, sb);
					i = range.lastMember.memIndex;
					from = range.aEnd;
					continue f1;
				}
				continue;
			}
			MethodRange mRange = (MethodRange) mem;
			Executable method = mRange.get();
			if (method != null && method.getName().equals("writeToDir"))
				Ca.pause();
			if (mRange.cls == null)
				Ca.asert(false, mRange);
			if (pre != null && pre == mRange)
				Ca.asert(false, mRange);
			//
			if (mRange.visited) {//usedMethod.get() instanceof Constructor<?>
				Ca.pause();
			} else if (Config26.TobeExtracted_methods.map.containsKey(mRange.name())) {
				Ca.pause();
			} else {
				if (from > mRange.aStart || mRange.aStart > fileSt.length()) {
					throw new RuntimeException("May be application executed on old version(unloged) source Code.");
				}
				//移除上一方法|属性到本方法之间的注解
				append_untilePreMemberEnd(i - 1, mRange, from, sb);
				from = mRange.aEnd;
			}
			if (pre != null) {
				if (pre.aEnd > mRange.aStart)
					Ca.asert(false, i);
			}
			pre = mRange;
		}
		sb.append(fileSt.substring(from));
		Ca.asert(sb.length() <= fileSt.length(), tofile.getName());
		File9.str2file(sb.toString(), tofile, null, false);
		Cont26.INST.extract(file.length(), sb.length());
	}
}
